import socket
import threading
import os
import sys
from collections import defaultdict
import platform
import mimetypes
import time
from pathlib import Path
import hashlib  # 导入hashlib模块
import sqlite3  # 数据库
import GV  # 引用本系统定义的全局变量


class MyException(Exception):
    pass


class Server(object):
    # 对象实例化时进行的初始化
    def __init__(self, HOST='', PORT=7777, Version='1.11'):
        self.Host = HOST
        self.Port = PORT
        self.Version = Version
        self.FileList = []  # 文件列表，包括
        self.lock = threading.Lock()

    # start listenning
    def ServerStart(self):
        try:
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.s.bind((self.Host, self.Port))
            self.s.listen(5)
            print('Server %s %s is listening on port %s' % (self.Version,
                                                            socket.gethostbyname(socket.gethostname()),  self.Port))
            self.InitServerDB()  # 初始数据库
            while True:
                soc, addr = self.s.accept()
                print('%s:%s connected' % (addr[0], addr[1]))
                thread = threading.Thread(
                    target=self.ServerHandler, args=(soc, addr))
                thread.start()
        except KeyboardInterrupt:
            print('\nShutting down the server..\nGood Bye!')
            try:
                sys.exit(0)
            except SystemExit:
                os._exit(0)

    # connect with a client
    def InitServerDB(self):  # 初始数据库
        try:
            dbFilename = GV.ServerDbFilename
            dbConn = sqlite3.connect(dbFilename)  # 数据库连接
            c = dbConn.cursor()
            _SqlStr = "DELETE FROM ServerUseBlockList"
            c.execute(_SqlStr)
            dbConn.commit()
            dbConn.close()
        except:
            print("error")
        return

    # 处理客户端发过来的请求
    def ServerHandler(self, soc, addr):
        # keep Server received request from client
        host = None
        port = None
        while True:
            try:
                # sleep(1)
                exceptionMethod = ''
                req = soc.recv(1024).decode()
                print('\nServer received request:\n%s' % req)
                lines = req.splitlines()
                version = lines[0].split(' ')[-1]
                if version != self.Version:
                    soc.sendall(str.encode(
                        '505 Version Not Supported %s\n' % self.Version))
                else:
                    _MethodStr = lines[0].split(' ')[0]
                    if _MethodStr == 'ADD':
                        exceptionMethod = 'ADD'
                        ipStr = lines[1].split(None, 1)[1]
                        portStr = int(lines[2].split(None, 1)[1])
                        # dictFileA = Code20_P2P_Comm.dictFileType.copy()
                        fileName = lines[0].split()[1]
                        fileHash = lines[0].split()[2]
                        fileType = lines[0].split()[3]
                        self.AddToFileList(
                            soc, addr, (ipStr, portStr), fileName, fileHash, int(fileType))
                    elif _MethodStr == 'LIST_Block':
                        exceptionMethod = 'LIST_Block'
                        if len(lines[0].split(' ')) > 1:
                            _BlockID = int(lines[0].split()[1])
                            self.SendBlockFileInfoToClient(
                                soc, _BlockID)  # 只返回指定区块的清单，限定长度
                    elif _MethodStr == 'LIST_Tran':
                        exceptionMethod = 'LIST_Tran'
                        self.SendTranFileInfoToClient(
                            soc)  # 返回最近的交易文件
                    else:
                        raise AttributeError('Method Not Match')
            except ConnectionError:
                _TimeStr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                print('%s:%s left. %s' % (addr[0], addr[1], _TimeStr))
                self.RemoveListNotAvailable(addr)
                soc.close()
                break
            except BaseException:
                try:
                    soc.sendall(str.encode(
                        '400 Bad Request at %s %s %s\n' % (exceptionMethod, time.time(), self.Version)))
                except ConnectionError:
                    _TimeStr = time.strftime(
                        "%Y-%m-%d %H:%M:%S", time.localtime())
                    print('%s:%s left. %s' % (addr[0], addr[1], _TimeStr))
                    self.RemoveListNotAvailable(addr)
                    soc.close()
                    break

    def RemoveListNotAvailable(self, addr):
        self.lock.acquire()
        ServerDbFilename = GV.ServerDbFilename
        dbConn = sqlite3.connect(ServerDbFilename)  # 数据库连接
        c = dbConn.cursor()
        _SqlStrInsert = "DELETE FROM ServerUseBlockList WHERE SUBL_SubmitIP='%s' AND SUBL_SubmitPort=%s;" % (
            addr[0], addr[1])
        c.execute(_SqlStrInsert)
        dbConn.commit()
        dbConn.close()
        self.lock.release()

    def AddToFileList(self, soc, addr, peer, fileName, fileHash, fileType):
        # 将clientA拥有的文件加入到server的文件列表中
        # 其他clientB可以请求服务器，获得文件列表。clientB可以通过P2P连接到clientA，下载clientA拥有的文件
        self.lock.acquire()
        try:
            ServerDbFilename = GV.ServerDbFilename
            dbConn = sqlite3.connect(ServerDbFilename)  # 数据库连接
            c = dbConn.cursor()
            if int(fileType) == 1:  # 区块文件
                _BlockID = int(fileName.split('_')[1])
                _SqlStrInsert = "INSERT OR REPLACE INTO ServerUseBlockList(SUBL_Filename,SUBL_Type,SUBL_Hash,SUBL_BlockID,SUBL_ProviderIP,SUBL_ProviderPort,SUBL_SubmitIP,SUBL_SubmitPort,SUBL_Timestampe) VALUES('%s',%s,'%s',%s,'%s',%s,'%s',%s,%s);" % (
                    fileName, fileType, fileHash, _BlockID, peer[0], peer[1], addr[0], addr[1], time.time())
                c.execute(_SqlStrInsert)
            if int(fileType) == 2:  # 交易文件
                _BlockID = -1
                _SqlStrInsert = "INSERT OR REPLACE INTO ServerUseBlockList(SUBL_Filename,SUBL_BlockID,SUBL_Hash,SUBL_Type,SUBL_ProviderIP,SUBL_ProviderPort,SUBL_SubmitIP,SUBL_SubmitPort,SUBL_Timestampe) VALUES('%s',%s,'%s',%s,'%s',%s,'%s',%s,%s);" % (
                    fileName, _BlockID, fileHash, fileType, peer[0], peer[1], addr[0], addr[1], time.time())
                c.execute(_SqlStrInsert)
            dbConn.commit()
            dbConn.close()
        finally:
            self.lock.release()
        header = '200 %s\n' % (self.Version)
        header += 'ADD %s %s %s %s %s\n' % (fileName, fileType,
                                            fileHash, peer[0], peer[1])
        soc.sendall(str.encode(header))
        print(header)

    def SendBlockFileInfoToClient(self, soc, BlockID):  # 只返回指定区块的清单，限定长度为
        self.lock.acquire()
        try:
            # region #通过数据库实现
            header = '404 Not Found %s\n' % self.Version
            dbFilename = GV.ServerDbFilename
            dbConn = sqlite3.connect(dbFilename)  # 数据库连接
            c = dbConn.cursor()
            _SelectSqlStr = "SELECT SUBL_Filename,SUBL_Type,SUBL_Hash,SUBL_ProviderIP,SUBL_ProviderPort FROM ServerUseBlockList WHERE SUBL_BlockID=%s ORDER BY SUBL_Timestampe DESC LIMIT 6;" % (
                int(BlockID))
            _Rows = c.execute(_SelectSqlStr)
            _HeaderStr2 = ''
            for _Row in _Rows:
                _HeaderStr2 += 'BlockFile %s %s %s %s %s %s\n' % (
                    _Row[0], _Row[1], _Row[2], BlockID, _Row[3], _Row[4])
            dbConn.close()
            _HeaderStr1 = '200 %s\n' % (self.Version)
            if len(_HeaderStr2) > 0:
                header = _HeaderStr1+_HeaderStr2
        #endregion
        finally:
            self.lock.release()
        soc.sendall(str.encode(header))
        print(header)

    def SendTranFileInfoToClient(self, soc):  # 只返回指定区块的清单，限定长度为
        self.lock.acquire()
        try:
            # region #通过数据库实现
            header = '404 Not Found %s\n' % self.Version
            dbFilename = GV.ServerDbFilename
            dbConn = sqlite3.connect(dbFilename)  # 数据库连接
            c = dbConn.cursor()
            _SelectSqlStr = "SELECT SUBL_Filename,SUBL_Type,SUBL_Hash,SUBL_ProviderIP,SUBL_ProviderPort FROM ServerUseBlockList WHERE SUBL_Type=2 ORDER BY SUBL_Timestampe DESC LIMIT 6;"
            _Rows = c.execute(_SelectSqlStr)
            header = '200 OK %s\n' % self.Version
            for _Row in _Rows:
                header += 'TranFile %s %s %s %s %s\n' % (
                    _Row[0], _Row[1], _Row[2], _Row[3], _Row[4])
            dbConn.close()
            #endregion
        finally:
            self.lock.release()
        soc.sendall(str.encode(header))
        print(header)


s = Server()
s.ServerStart()
